/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package javafxapplication1;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.google.common.base.Joiner;
import com.google.common.io.Files;
import com.jfoenix.controls.JFXRadioButton;
import com.jfoenix.controls.JFXTextArea;
import com.jfoenix.controls.JFXTextField;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

/**
 * FXML Controller class
 *
 * @author my
 */
public class ValidationFXMLController implements Initializable {

    Stage stage;

    @FXML
    private ResourceBundle resources;
    private Locale locale = Locale.getDefault();
    private File xlsFile;

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {

        loadProps();

        listViewCols.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        tableColumnName.setCellValueFactory((TableColumn.CellDataFeatures<ColumnInfo, String> param) -> param.getValue().nameProperty());

        listViewCols.getSelectionModel().selectedItemProperty().addListener((observable, oldVal, newVal) -> {
            final ColumnInfo c = observable.getValue();
            if (c == null) {
                return;
            }
            lblName.setText(c.getName());
            txtDefaultVal.setText(c.getDefaultVal());
            txtComments.setText(c.getComments());
            txtMin.setText(c.getMin());
            txtMax.setText(c.getMax());
            txtSequence.setText(c.getSequence());
            txtPromptTitle.setText(c.getPromptTitle());
            txtpromptContent.setText(c.getPromptContent());

            switch (c.getType()) {
                case 0:
                    rdoString.setSelected(true);
                    break;
                case 1:
                    rdoNumber.setSelected(true);
                    break;
                case 2:
                    rdoSequence.setSelected(true);
                    break;
                default:
                    rdoSequence.setSelected(false);
                    rdoNumber.setSelected(false);
                    rdoString.setSelected(false);
                    txtDefaultVal.setDisable(true);
            }
            if (rdoSequence.isSelected() || rdoString.isSelected()) {
                String regex = "(" + getText("range.title") + "(.*))$*\n*";
                final String text = txtpromptContent.getText();
                final Matcher matcher = Pattern.compile(regex).matcher(text);
                if (matcher.find()) {
                    txtpromptContent.setText(text.replaceAll(matcher.group(1), ""));
                }
            }
        });

        txtDefaultVal.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            selectedItem.setDefaultVal(newValue);

        });
        txtComments.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            selectedItem.setComments(newValue);

        });
        txtMin.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            selectedItem.setMin(newValue);

        });
        txtMax.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            selectedItem.setMax(newValue);

        });
        txtSequence.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            selectedItem.setSequence(newValue);

        });
        txtPromptTitle.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            if (selectedItem != null) {
                selectedItem.setPromptTitle(newValue);
            }
        });
        txtpromptContent.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            selectedItem.setPromptContent(newValue);

        });

        txtSequence.disableProperty().bind(Bindings.not(rdoSequence.selectedProperty()));
        txtMin.disableProperty().bind(Bindings.not(rdoNumber.selectedProperty()));
        txtMax.disableProperty().bind(Bindings.not(rdoNumber.selectedProperty()));

        txtPromptTitle.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {

                final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
                selectedItem.setPromptTitle(newValue);
                ;
            }
        });

        rdoString.selectedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            selectedItem.setType(0);
            txtPromptTitle.setText(getText("text.title.string"));
            txtDefaultVal.setDisable(false);
        });
        rdoNumber.selectedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            selectedItem.setType(1);
            txtPromptTitle.setText(getText("text.title.number"));
            txtDefaultVal.setDisable(false);
        });
        rdoSequence.selectedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
            final ColumnInfo selectedItem = listViewCols.getSelectionModel().getSelectedItem();
            selectedItem.setType(2);
            txtPromptTitle.setText(getText("text.title.sequence"));
            txtDefaultVal.setDisable(false);
        });

    }

    @FXML
    private JFXRadioButton rdoLangZh;

    @FXML
    private ToggleGroup langGroup;

    @FXML
    private JFXRadioButton rdoLangEn;
    @FXML
    private JFXRadioButton rdoSequence;
    @FXML
    private JFXTextArea txtSequence;

    @FXML
    private ToggleGroup cellTypeGroup;

    @FXML
    private JFXTextField txtPromptTitle;
    @FXML
    private Label lblName;

    @FXML
    private JFXRadioButton rdoNumber;

    @FXML
    private JFXRadioButton rdoString;

    @FXML
    private JFXTextField txtDefaultVal;

    @FXML
    private JFXTextField txtMin;

    @FXML
    private JFXTextField txtMax;

    @FXML
    private JFXTextArea txtpromptContent;
    @FXML
    private AnchorPane anchorPaneMainContent;
    @FXML
    private JFXTextArea txtComments;

    @FXML
    private TableView<ColumnInfo> listViewCols;

    @FXML
    private TableColumn<ColumnInfo, String> tableColumnName;
    @FXML
    private ImageView imageViewUp;

    @FXML
    private ImageView imageViewDown;

    @FXML
    private ImageView imageViewOpenExcel;
    @FXML
    private ContextMenu tableViewContextMenu;

    @FXML
    private MenuItem menuItemDeleteSelected;
    @FXML
    private ImageView imageViewDefaultVal;

    @FXML
    private ImageView imageViewMin;

    @FXML
    private ImageView imageViewMax;

    @FXML
    private ImageView imageViewSequence;
    @FXML
    private ImageView imageViewSettings;

    @FXML
    private AnchorPane anchorPaneSettings;

    @FXML
    private JFXTextField txtSettingTitleOffset;

    @FXML
    private JFXTextField txtSettingSequenceSplitChar;

    @FXML
    private JFXTextField txtSettingDataRowOffset;

    @FXML
    void doShowSettings(MouseEvent event) {

        anchorPaneSettings.setVisible(anchorPaneMainContent.isVisible());
        anchorPaneMainContent.setVisible(!anchorPaneMainContent.isVisible());
    }

    @FXML
    void doUpdatePromptContent(MouseEvent event) {
        String content = "";

        updateDefaultVal();

        if (rdoNumber.isSelected()) {
            updateRangeVal();
        } else if (rdoSequence.isSelected()) {
            updateSequence();

        }

    }

    public void updateDefaultVal() {
        String text = txtpromptContent.getText();
        Matcher matcher = Pattern.compile("(" + getText("default.title") + "(.*))$*\n*").matcher(text);
        if (matcher.find()) {
            String group = matcher.group(1);
            String replaceFirst = text.replaceFirst(group, getText("default.title") + txtDefaultVal.getText());
            txtpromptContent.setText(replaceFirst);
        } else {
            txtpromptContent.setText(getText("default.title") + txtDefaultVal.getText() + "\n" + text);
        }

    }

    public void updateRangeVal() {
        String text = txtpromptContent.getText();
        Matcher matcher = Pattern.compile("(" + getText("range.title") + "(.*))$*\n*").matcher(text);
        if (matcher.find()) {
            String group = matcher.group(1);
            System.out.println("group=" + group);
            String replaceFirst = text.replaceFirst(group, getText("range.title") + String.format(getText("range.format"), txtMin.getText(), txtMax.getText()));
            txtpromptContent.setText(replaceFirst);
        } else {
            txtpromptContent.appendText("\n" + getText("range.title") + String.format(getText("range.format"), txtMin.getText(), txtMax.getText()));
        }
    }

    public void updateSequence() {
        String content = "";
        String text = txtSequence.getText();
        Pattern compile = Pattern.compile("(\\d+)-(\\d+)$");
        Matcher matcher = compile.matcher(text);

        if (matcher.find()) {
            String min = matcher.group(1);
            String max = matcher.group(2);
            String range = "";
            if (min.matches("\\d+") && max.matches("\\d+")) {
                if (Integer.parseInt(min) < Integer.parseInt(max)) {
                    for (int i = Integer.parseInt(min); i <= Integer.parseInt(max); i++) {
                        range += i + "\n";
                    }
                }
                text = text.replaceFirst(matcher.group(0), range);
            }
        }

        txtpromptContent.appendText(text.replaceAll("\n", " - \n"));

    }

    @FXML
    void doDeleteSelected(ActionEvent event) {
        listViewCols.getItems().removeAll(listViewCols.getSelectionModel().getSelectedItems());
    }

    @FXML
    void doNextRecord(MouseEvent event) {
        int nextRow = listViewCols.getSelectionModel().getSelectedIndex() + 1;
        if (nextRow < listViewCols.getItems().size()) {
            listViewCols.getSelectionModel().clearAndSelect(nextRow);
        }
    }

    @FXML
    void doUpRecord(MouseEvent event) {
        int preRow = listViewCols.getSelectionModel().getSelectedIndex() - 1;
        if (preRow > -1) {
            listViewCols.getSelectionModel().clearAndSelect(preRow);
        }
    }

    @FXML
    void doOpenExcel(MouseEvent event) {
        FileChooser fileChooser = new FileChooser();
        final File showOpenDialog = fileChooser.showOpenDialog(stage.getOwner());
        if (showOpenDialog != null) {
            try {
                xlsFile = showOpenDialog;
                final File ruleFile = getRuleFile();

                if (ruleFile != null && ruleFile.exists() && new Alert(Alert.AlertType.CONFIRMATION, getText("是否继续上次编辑？"), ButtonType.YES, ButtonType.NO).showAndWait().get() == ButtonType.YES) {
                    final JSONArray parseArray = JSON.parseArray(Files.toString(ruleFile, Charset.defaultCharset()));
                    ObservableList<ColumnInfo> items = listViewCols.getItems();
                    if (items != null) {
                        items.clear();
                    }
                    for (int i = 0; i < parseArray.size(); i++) {
                        final Object get = parseArray.get(i);
                        final ColumnInfoWrapper parseObject = JSON.parseObject(get.toString(), ColumnInfoWrapper.class);
                        final ColumnInfo unwrap = ColumnInfoWrapper.unwrap(parseObject);
                        listViewCols.getItems().add(unwrap);
                    }
                } else {
                    final List<ColumnInfo> cols = new ExcelReader().read(showOpenDialog);
                    ObservableList<ColumnInfo> items = listViewCols.getItems();
                    if (items != null) {
                        items.clear();
                    }
                    listViewCols.getItems().addAll(cols);
                }

                anchorPaneMainContent.setVisible(true);

            } catch (IOException ex) {
                Logger.getLogger(ValidationFXMLController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    @FXML
    void doSaveDataValidationToExcel(MouseEvent event) {
        storeDataState();
        final ValidationWriter validationWriter = new ValidationWriter();
        try {
            final Result r = validationWriter.write(xlsFile, listViewCols.getItems());
            if (r.getErrors().isEmpty()) {
                final File out = r.getOut();
                FileChooser chooser = new FileChooser();
                chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("excel2003 file", ".xls"));

                chooser.setInitialDirectory(xlsFile.getParentFile());
                chooser.setInitialFileName(xlsFile.getParentFile() + File.separator + Files.getNameWithoutExtension(xlsFile.getAbsolutePath()) + "-datavalidation.xls");
                final File asSaveFile = chooser.showSaveDialog(stage.getOwner());
                if (asSaveFile != null) {
                    try {
                        final boolean setReadable = asSaveFile.setReadable(false);
                        System.out.println("setReadable=" + setReadable);
                        final boolean setWritable = asSaveFile.setWritable(true, false);
                        System.out.println("setWritable=" + setWritable);
                        Files.move(out, asSaveFile);
                        new Alert(AlertType.INFORMATION, "保持文件成功!", ButtonType.OK).show();
                    } catch (Exception e) {
                        new Alert(AlertType.ERROR, e.getMessage(), ButtonType.OK).show();
                    }

                }
            } else {
                new Alert(AlertType.ERROR, Joiner.on("\n").join(r.getErrors().toArray()), ButtonType.OK).show();
            }
        } catch (Exception ex) {
            Logger.getLogger(ValidationFXMLController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Save UI data to disk
     *
     *
     */
    public void storeDataState() {
        final ObservableList<ColumnInfo> items = listViewCols.getItems();

        List<ColumnInfoWrapper> list = new ArrayList<>(items.size());
        items.forEach((t) -> {
            list.add(ColumnInfoWrapper.wrap(t));
        });

        if (xlsFile != null) {

            try {
                final String toJson = JSON.toJSONString(list);
//                System.out.println("toJson=" + toJson);
                final String nameWithoutExtension = Files.getNameWithoutExtension(xlsFile.getAbsolutePath());
                final File dataFile = new File(xlsFile.getParentFile().getAbsolutePath() + File.separator + nameWithoutExtension + "-rules.dat");
//                System.out.println(dataFile.getAbsolutePath());
                Files.write(toJson, dataFile, Charset.defaultCharset());
            } catch (IOException ex) {
                Logger.getLogger(ValidationFXMLController.class.getName()).log(Level.SEVERE, null, ex);
            }
            //System.out.println("save data to disk successfully!");
        }

    }

    public File getRuleFile() {
        if (xlsFile != null) {
            final String nameWithoutExtension = Files.getNameWithoutExtension(xlsFile.getAbsolutePath());
            File dataFile = new File(xlsFile.getParentFile().getAbsolutePath() + File.separator + nameWithoutExtension + "-rules.dat");
            return dataFile;
        } else {
            return null;
        }
    }

    /**
     * 动态切换语言用的
     *
     * @param key
     * @return
     */
    public String getText(String key) {
        final ResourceBundle bundle = ResourceBundle.getBundle(JavaFXApplication1.resourceBundleName, locale);
        if (bundle.containsKey(key)) {
            return bundle.getString(key);
        } else {
            return key;
        }
    }

    @FXML
    void doChangeEn(ActionEvent event) {
        this.locale = Locale.ENGLISH;
    }

    @FXML
    void doChangeZh(ActionEvent event) {
        this.locale = Locale.CHINA;
    }

    public Stage getStage() {
        return stage;
    }

    public void setStage(Stage stage) {
        this.stage = stage;
    }

    private void loadProps() {
        String titleOffset = txtSettingTitleOffset.getText();
        if (titleOffset.isEmpty() || !titleOffset.matches("\\d+") || Long.parseLong(titleOffset) > 60000 || Long.parseLong(titleOffset) < 0) {
            titleOffset = "1";
        }
        String dataRowOffset = txtSettingDataRowOffset.getText();
        if (dataRowOffset.isEmpty() || !dataRowOffset.matches("\\d+") || Long.parseLong(dataRowOffset) > 60000 || Long.parseLong(dataRowOffset) < Long.parseLong(titleOffset)) {
            titleOffset = "2";
        }

        String seqSplitChar = txtSettingSequenceSplitChar.getText();
        if (seqSplitChar.matches("\\w+")) {

        } else {
            seqSplitChar = " - ";
        }

        Profile.put("setting.titleRowOffset", titleOffset);
        Profile.put("setting.dataRowOffset", dataRowOffset);
        Profile.put("setting.sequence.splitChar", seqSplitChar);
    }

}
